D:\a\vk-layer-for-rust\vk-layer-for-rust\vulkan-layer\src\lib.rs
Line | Count | Source |
1 | | // Copyright 2023 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #![warn(missing_docs)] |
16 | | #![cfg_attr(all(doc, RUSTC_NIGHTLY), feature(doc_auto_cfg))] |
17 | | |
18 | | //! This crate provides a convenient framework to develop |
19 | | //! [Vulkan layers](https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderLayerInterface.md) |
20 | | //! in Rust on top of the [ash](https://crates.io/crates/ash) crate. If you are not familiar how to |
21 | | //! write a Vulkan layer, [this C++ tutorial](https://renderdoc.org/vulkan-layer-guide.html) by |
22 | | //! Baldur Karlsson is a good reference to start with. |
23 | | //! |
24 | | //! Key features provided by this crate includes: |
25 | | //! |
26 | | //! * Support the look-up map fashion of implementing a Vulkan layer. |
27 | | //! |
28 | | //! The look-up maps for `VkDevice` and `VkInstance` are handled by this crate. |
29 | | //! |
30 | | //! * Implement `vkGet*ProcAddr` automatically. |
31 | | //! |
32 | | //! This is a non-trivial work to comply with the spec, because of extensions and the |
33 | | //! required/supported Vulkan API level. See the |
34 | | //! [spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html) |
35 | | //! of `vkGetInstanceProc` for details. |
36 | | //! |
37 | | //! * Handle dispatch tables, `vkCreateInstance` and `vkCreateDevice`. |
38 | | //! |
39 | | //! This mainly includes using the `vkGet*ProcAddr` function pointer correctly, and advancing the |
40 | | //! `VkLayer*CreateInfo::u::pLayerInfo` link list. One common mistake in layer implementation |
41 | | //! in `vkCreateDevice` is to call `getInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice")` to |
42 | | //! obtain the function pointer to `vkCreateDevice`. According to |
43 | | //! [the spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#_description), |
44 | | //! `getInstanceProcAddr` should return `NULL`. This framework helps you avoid bugs like this. |
45 | | //! |
46 | | //! Note that object wrapping is not supported by this crate, and we don't plan such support, |
47 | | //! because object wrapping requires us to intercept ALL Vulkan commands related to one handle type, |
48 | | //! so it can't handle unknown commands. In addition, object wrapping is more complicated because it |
49 | | //! is required to call loader callback on every dispatchable handle creation and destruction. |
50 | | //! |
51 | | //! # Overview |
52 | | //! |
53 | | //! The primary types in this crate are the [`Layer`] trait and the [`Global`] struct. The user |
54 | | //! implements the [`Layer`] trait for a type `T`, and the [`Global<T>`] will include all necessary |
55 | | //! functions needed to export from this dynamic link library. With the help of the |
56 | | //! [`declare_introspection_queries`] macro, the user can export those functions in oneline. |
57 | | //! |
58 | | //! ``` |
59 | | //! use ash::{self, vk}; |
60 | | //! use once_cell::sync::Lazy; |
61 | | //! use std::sync::Arc; |
62 | | //! use vulkan_layer::{ |
63 | | //! declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo, |
64 | | //! StubGlobalHooks, StubInstanceInfo, |
65 | | //! }; |
66 | | //! |
67 | | //! // Define the layer type. |
68 | | //! #[derive(Default)] |
69 | | //! struct MyLayer(StubGlobalHooks); |
70 | | //! |
71 | | //! // Implement the Layer trait. |
72 | | //! impl Layer for MyLayer { |
73 | | //! type GlobalHooksInfo = StubGlobalHooks; |
74 | | //! type InstanceInfo = StubInstanceInfo; |
75 | | //! type DeviceInfo = StubDeviceInfo; |
76 | | //! type InstanceInfoContainer = StubInstanceInfo; |
77 | | //! type DeviceInfoContainer = StubDeviceInfo; |
78 | | //! |
79 | | //! fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static { |
80 | | //! static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default); |
81 | | //! &*GLOBAL |
82 | | //! } |
83 | | //! |
84 | | //! fn manifest() -> LayerManifest { |
85 | | //! Default::default() |
86 | | //! } |
87 | | //! |
88 | | //! fn global_hooks_info(&self) -> &Self::GlobalHooksInfo { |
89 | | //! &self.0 |
90 | | //! } |
91 | | //! |
92 | | //! fn create_instance_info( |
93 | | //! &self, |
94 | | //! _: &vk::InstanceCreateInfo, |
95 | | //! _: Option<&vk::AllocationCallbacks>, |
96 | | //! _: Arc<ash::Instance>, |
97 | | //! _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, |
98 | | //! ) -> Self::InstanceInfoContainer { |
99 | | //! Default::default() |
100 | | //! } |
101 | | //! |
102 | | //! fn create_device_info( |
103 | | //! &self, |
104 | | //! _: vk::PhysicalDevice, |
105 | | //! _: &vk::DeviceCreateInfo, |
106 | | //! _: Option<&vk::AllocationCallbacks>, |
107 | | //! _: Arc<ash::Device>, |
108 | | //! _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr, |
109 | | //! ) -> Self::DeviceInfoContainer { |
110 | | //! Default::default() |
111 | | //! } |
112 | | //! } |
113 | | //! |
114 | | //! // Define the global type from the layer type. |
115 | | //! type MyGlobal = Global<MyLayer>; |
116 | | //! // Export C functions. |
117 | | //! declare_introspection_queries!(MyGlobal); |
118 | | //! ``` |
119 | | //! The user can provide their own types that implement different traits to intercept different |
120 | | //! commands and custom the layer behavior: |
121 | | //! * [`GlobalHooks`], [`InstanceHooks`], [`DeviceHooks`]: Implements the interception to different |
122 | | //! Vulkan commands based on the dispatch types. |
123 | | //! * [`GlobalHooksInfo`], [`InstanceInfo`], [`DeviceInfo`]: Container of [`GlobalHooks`], |
124 | | //! [`InstanceHooks`], [`DeviceHooks`]. Provide the list of commands to intercept for the |
125 | | //! framework. Can be automatically implemented through the [`auto_deviceinfo_impl`], |
126 | | //! [`auto_globalhooksinfo_impl`], [`auto_instanceinfo_impl`] macros from the [`GlobalHooks`], |
127 | | //! [`InstanceHooks`], [`DeviceHooks`] implementation respectively. |
128 | | //! * [`Layer`]: Provide the metadata of a layer through [`LayerManifest`], e.g. the name, version, |
129 | | //! exported extensions of the layer. Provide the storage for the [`Global`] object. Container of |
130 | | //! [`GlobalHooksInfo`] type. The factory type of the [`InstanceInfo`], [`DeviceInfo`] types. |
131 | | //! |
132 | | //! # Usage |
133 | | //! |
134 | | //! You can check a live example in `examples/hello-world`. |
135 | | //! |
136 | | //! First, create a Rust lib crate. |
137 | | //! ```bash |
138 | | //! $ mkdir vulkan-layer-rust-example |
139 | | //! $ cd vulkan-layer-rust-example |
140 | | //! $ cargo init --lib |
141 | | //! Created library package |
142 | | //! ``` |
143 | | //! |
144 | | //! Second, modify the crate type to `cdylib` and set the panic behavior to abort in `Cargo.toml`. |
145 | | //! ```toml |
146 | | //! [lib] |
147 | | //! crate-type = ["cdylib"] |
148 | | //! |
149 | | //! [profile.dev] |
150 | | //! panic = "abort" |
151 | | //! |
152 | | //! [profile.release] |
153 | | //! panic = "abort" |
154 | | //! ``` |
155 | | //! We need to set panic to abort to avoid unwinding from Rust to the caller(most likely C/C++), |
156 | | //! because unwinding into other language from Rust is |
157 | | //! [undefined behavior](https://doc.rust-lang.org/beta/nomicon/unwinding.html). |
158 | | //! |
159 | | //! If you want to try the layer on Android, also modify the crate name to |
160 | | //! `VkLayer_vendor_rust_example`, because Android requires that the layer shared object library |
161 | | //! follow a specific name convention. |
162 | | //! ```toml |
163 | | //! [lib] |
164 | | //! name = "VkLayer_vendor_rust_example" |
165 | | //! ``` |
166 | | //! |
167 | | //! Third, set up the dependency in `Cargo.toml`. In my case, I checkout the project repository in |
168 | | //! the same directory where the `vulkan-layer-rust-example` folder lives. |
169 | | //! ```toml |
170 | | //! [dependencies] |
171 | | //! vulkan-layer = { path = "../vk-layer-for-rust/vulkan-layer" } |
172 | | //! ``` |
173 | | //! Other dependencies. |
174 | | //! ```bash |
175 | | //! cargo add ash once_cell |
176 | | //! ``` |
177 | | //! |
178 | | //! Fourth, implement the layer trait in `lib.rs`. |
179 | | //! ``` |
180 | | //! use ash::{self, vk}; |
181 | | //! use once_cell::sync::Lazy; |
182 | | //! use std::sync::Arc; |
183 | | //! use vulkan_layer::{ |
184 | | //! declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo, |
185 | | //! StubGlobalHooks, StubInstanceInfo, |
186 | | //! }; |
187 | | //! |
188 | | //! #[derive(Default)] |
189 | | //! struct MyLayer(StubGlobalHooks); |
190 | | //! |
191 | | //! impl Layer for MyLayer { |
192 | | //! type GlobalHooksInfo = StubGlobalHooks; |
193 | | //! type InstanceInfo = StubInstanceInfo; |
194 | | //! type DeviceInfo = StubDeviceInfo; |
195 | | //! type InstanceInfoContainer = StubInstanceInfo; |
196 | | //! type DeviceInfoContainer = StubDeviceInfo; |
197 | | //! |
198 | | //! fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static { |
199 | | //! static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default); |
200 | | //! &*GLOBAL |
201 | | //! } |
202 | | //! |
203 | | //! fn manifest() -> LayerManifest { |
204 | | //! let mut manifest = LayerManifest::default(); |
205 | | //! manifest.name = "VK_LAYER_VENDOR_rust_example"; |
206 | | //! manifest.spec_version = vk::API_VERSION_1_1; |
207 | | //! manifest.description = "Rust test layer"; |
208 | | //! manifest |
209 | | //! } |
210 | | //! |
211 | | //! fn global_hooks_info(&self) -> &Self::GlobalHooksInfo { |
212 | | //! &self.0 |
213 | | //! } |
214 | | //! |
215 | | //! fn create_instance_info( |
216 | | //! &self, |
217 | | //! _: &vk::InstanceCreateInfo, |
218 | | //! _: Option<&vk::AllocationCallbacks>, |
219 | | //! _: Arc<ash::Instance>, |
220 | | //! _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, |
221 | | //! ) -> Self::InstanceInfoContainer { |
222 | | //! Default::default() |
223 | | //! } |
224 | | //! |
225 | | //! fn create_device_info( |
226 | | //! &self, |
227 | | //! _: vk::PhysicalDevice, |
228 | | //! _: &vk::DeviceCreateInfo, |
229 | | //! _: Option<&vk::AllocationCallbacks>, |
230 | | //! _: Arc<ash::Device>, |
231 | | //! _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr, |
232 | | //! ) -> Self::DeviceInfoContainer { |
233 | | //! println!("Hello from the Rust Vulkan layer!"); |
234 | | //! Default::default() |
235 | | //! } |
236 | | //! } |
237 | | //! ``` |
238 | | //! |
239 | | //! Fifth, export functions through the [`declare_introspection_queries`] macro |
240 | | //! ``` |
241 | | //! # use ash::{self, vk}; |
242 | | //! # use once_cell::sync::Lazy; |
243 | | //! # use std::sync::Arc; |
244 | | //! # use vulkan_layer::{ |
245 | | //! # declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo, |
246 | | //! # StubGlobalHooks, StubInstanceInfo, |
247 | | //! # }; |
248 | | //! # |
249 | | //! # #[derive(Default)] |
250 | | //! # struct MyLayer(StubGlobalHooks); |
251 | | //! # |
252 | | //! # impl Layer for MyLayer { |
253 | | //! # type GlobalHooksInfo = StubGlobalHooks; |
254 | | //! # type InstanceInfo = StubInstanceInfo; |
255 | | //! # type DeviceInfo = StubDeviceInfo; |
256 | | //! # type InstanceInfoContainer = StubInstanceInfo; |
257 | | //! # type DeviceInfoContainer = StubDeviceInfo; |
258 | | //! # |
259 | | //! # fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static { |
260 | | //! # static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default); |
261 | | //! # &*GLOBAL |
262 | | //! # } |
263 | | //! # |
264 | | //! # fn manifest() -> LayerManifest { |
265 | | //! # let mut manifest = LayerManifest::default(); |
266 | | //! # manifest.name = "VK_LAYER_VENDOR_rust_example"; |
267 | | //! # manifest.spec_version = vk::API_VERSION_1_1; |
268 | | //! # manifest.description = "Rust test layer"; |
269 | | //! # manifest |
270 | | //! # } |
271 | | //! # |
272 | | //! # fn global_hooks_info(&self) -> &Self::GlobalHooksInfo { |
273 | | //! # &self.0 |
274 | | //! # } |
275 | | //! # |
276 | | //! # fn create_instance_info( |
277 | | //! # &self, |
278 | | //! # _: &vk::InstanceCreateInfo, |
279 | | //! # _: Option<&vk::AllocationCallbacks>, |
280 | | //! # _: Arc<ash::Instance>, |
281 | | //! # _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, |
282 | | //! # ) -> Self::InstanceInfoContainer { |
283 | | //! # Default::default() |
284 | | //! # } |
285 | | //! # |
286 | | //! # fn create_device_info( |
287 | | //! # &self, |
288 | | //! # _: vk::PhysicalDevice, |
289 | | //! # _: &vk::DeviceCreateInfo, |
290 | | //! # _: Option<&vk::AllocationCallbacks>, |
291 | | //! # _: Arc<ash::Device>, |
292 | | //! # _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr, |
293 | | //! # ) -> Self::DeviceInfoContainer { |
294 | | //! # println!("Hello from the Rust Vulkan layer!"); |
295 | | //! # Default::default() |
296 | | //! # } |
297 | | //! # } |
298 | | //! declare_introspection_queries!(Global::<MyLayer>); |
299 | | //! ``` |
300 | | //! |
301 | | //! Sixth, build and check the exported symbol of the build artifacts, and we should see Vulkan |
302 | | //! introspection APIs are exported. |
303 | | //! |
304 | | //! On Windows, use |
305 | | //! [`dumpbin`](https://learn.microsoft.com/en-us/cpp/build/reference/dumpbin-reference?view=msvc-170) |
306 | | //! ```cmd |
307 | | //! $ dumpbin /exports .\target\debug\VkLayer_vendor_rust_example.dll |
308 | | //! Microsoft (R) COFF/PE Dumper Version 14.36.32537.0 |
309 | | //! Copyright (C) Microsoft Corporation. All rights reserved. |
310 | | //! |
311 | | //! |
312 | | //! Dump of file .\target\debug\VkLayer_vendor_rust_example.dll |
313 | | //! |
314 | | //! File Type: DLL |
315 | | //! |
316 | | //! Section contains the following exports for VkLayer_vendor_rust_example.dll |
317 | | //! |
318 | | //! 00000000 characteristics |
319 | | //! FFFFFFFF time date stamp |
320 | | //! 0.00 version |
321 | | //! 1 ordinal base |
322 | | //! 6 number of functions |
323 | | //! 6 number of names |
324 | | //! |
325 | | //! ordinal hint RVA name |
326 | | //! |
327 | | //! 1 0 000F1840 vkEnumerateDeviceExtensionProperties = vkEnumerateDeviceExtensionProperties |
328 | | //! 2 1 000F1820 vkEnumerateDeviceLayerProperties = vkEnumerateDeviceLayerProperties |
329 | | //! 3 2 000F1800 vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties |
330 | | //! 4 3 000F17E0 vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties |
331 | | //! 5 4 000F1890 vkGetDeviceProcAddr = vkGetDeviceProcAddr |
332 | | //! 6 5 000F1870 vkGetInstanceProcAddr = vkGetInstanceProcAddr |
333 | | //! |
334 | | //! Summary |
335 | | //! |
336 | | //! 1000 .data |
337 | | //! 11000 .pdata |
338 | | //! 37000 .rdata |
339 | | //! 2000 .reloc |
340 | | //! 171000 .text |
341 | | //! ``` |
342 | | //! On Linux, use [`objdump`](https://linux.die.net/man/1/objdump). |
343 | | //! ```bat |
344 | | //! $ objdump -TC target/debug/libVkLayer_vendor_rust_example.so |
345 | | //! |
346 | | //! target/debug/libVkLayer_vendor_rust_example.so: file format elf64-x86-64 |
347 | | //! |
348 | | //! DYNAMIC SYMBOL TABLE: |
349 | | //! (omit some irrelevant symbols...) |
350 | | //! 00000000000fad20 g DF .text 0000000000000022 Base vkEnumerateDeviceExtensionProperties |
351 | | //! 00000000000face0 g DF .text 000000000000001c Base vkEnumerateInstanceExtensionProperties |
352 | | //! 00000000000fad50 g DF .text 0000000000000018 Base vkGetInstanceProcAddr |
353 | | //! 00000000000facc0 g DF .text 0000000000000018 Base vkEnumerateInstanceLayerProperties |
354 | | //! 00000000000fad00 g DF .text 000000000000001c Base vkEnumerateDeviceLayerProperties |
355 | | //! 00000000000fad70 g DF .text 0000000000000018 Base vkGetDeviceProcAddr |
356 | | //! ``` |
357 | | //! |
358 | | //! Seventh, create the layer manifest file named `rust_example_layer.json` right beside the built |
359 | | //! artifact. If targeting Android, the json manifest file is not needed. |
360 | | //! |
361 | | //! For Windows, |
362 | | //! ```json |
363 | | //! { |
364 | | //! "file_format_version" : "1.2.1", |
365 | | //! "layer": { |
366 | | //! "name": "VK_LAYER_VENDOR_rust_example", |
367 | | //! "type": "INSTANCE", |
368 | | //! "library_path": ".\\VkLayer_vendor_rust_example.dll", |
369 | | //! "library_arch" : "64", |
370 | | //! "api_version" : "1.1.0", |
371 | | //! "implementation_version" : "0", |
372 | | //! "description" : "Rust test layer" |
373 | | //! } |
374 | | //! } |
375 | | //! ``` |
376 | | //! |
377 | | //! For Linux, |
378 | | //! ```json |
379 | | //! { |
380 | | //! "file_format_version" : "1.2.1", |
381 | | //! "layer": { |
382 | | //! "name": "VK_LAYER_VENDOR_rust_example", |
383 | | //! "type": "INSTANCE", |
384 | | //! "library_path": "./libVkLayer_vendor_rust_example.so", |
385 | | //! "library_arch" : "64", |
386 | | //! "api_version" : "1.1.0", |
387 | | //! "implementation_version" : "0", |
388 | | //! "description" : "Rust test layer" |
389 | | //! } |
390 | | //! } |
391 | | //! ``` |
392 | | //! This json file will define an explicit layer named `VK_LAYER_VENDOR_rust_example`. |
393 | | //! |
394 | | //! Eighth, use [`VkConfig`](https://github.com/LunarG/VulkanTools/blob/main/vkconfig/README.md) |
395 | | //! (i.e. Vulkan Configurator) to force enable this explicit layer, and launch the vkcube |
396 | | //! application through `VkConfig`. In the log view, we should see the |
397 | | //! `"Hello from the Rust Vulkan layer!"` log line. For Android, follow |
398 | | //! [this instruction](https://developer.android.com/ndk/guides/graphics/validation-layer) to enable |
399 | | //! the layer. [`println!`] won't write to logcat, so one needs to change the layer implementation |
400 | | //! to write to the logcat. |
401 | | //! |
402 | | //! # Global initialization and clean up |
403 | | //! |
404 | | //! See [the][Layer#initialization] [document][Layer#global-clean-up] of [`Layer`] for details. |
405 | | //! |
406 | | //! # Extensions |
407 | | //! |
408 | | //! TODO |
409 | | //! |
410 | | //! # Synchronization |
411 | | //! |
412 | | //! TODO |
413 | | //! |
414 | | //! # Safety |
415 | | //! |
416 | | //! TODO |
417 | | //! |
418 | | //! # API stability |
419 | | //! |
420 | | //! TODO |
421 | | |
422 | | use ash::vk::{self, Handle}; |
423 | | use bytemuck::cast_slice; |
424 | | use log::{error, warn}; |
425 | | use std::{ |
426 | | borrow::Borrow, |
427 | | collections::{BTreeMap, BTreeSet}, |
428 | | ffi::{c_char, c_void, CStr, CString}, |
429 | | ptr::{null, null_mut, NonNull}, |
430 | | sync::{Arc, Mutex}, |
431 | | }; |
432 | | extern crate self as vulkan_layer; |
433 | | |
434 | | mod bindings; |
435 | | mod global_simple_intercept; |
436 | | mod layer_trait; |
437 | | mod lazy_collection; |
438 | | #[cfg(any(feature = "_test", test))] |
439 | | pub mod test_utils; |
440 | | |
441 | | #[cfg(feature = "unstable")] |
442 | | pub mod unstable_api; |
443 | | #[cfg(not(feature = "unstable"))] |
444 | | mod unstable_api; |
445 | | mod vk_utils; |
446 | | |
447 | | use bindings::vk_layer::{VkLayerDeviceCreateInfo, VkLayerFunction, VkLayerInstanceCreateInfo}; |
448 | | pub use bindings::vk_layer::{VkLayerDeviceLink, VkLayerInstanceLink}; |
449 | | pub use global_simple_intercept::Extension; |
450 | | use global_simple_intercept::{DeviceDispatchTable, InstanceDispatchTable, VulkanCommand}; |
451 | | pub use layer_trait::{ |
452 | | DeviceHooks, DeviceInfo, ExtensionProperties, GlobalHooks, GlobalHooksInfo, InstanceHooks, |
453 | | InstanceInfo, Layer, LayerManifest, LayerResult, VulkanCommand as LayerVulkanCommand, |
454 | | }; |
455 | | use unstable_api::{ApiVersion, IsCommandEnabled, LazyCollection}; |
456 | | pub use vk_utils::{fill_vk_out_array, VulkanBaseInStructChain, VulkanBaseOutStructChain}; |
457 | | use vk_utils::{ptr_as_uninit_mut, slice_from_raw_parts, slice_to_owned_strings}; |
458 | | pub use vulkan_layer_macros::{ |
459 | | auto_deviceinfo_impl, auto_globalhooksinfo_impl, auto_instanceinfo_impl, |
460 | | declare_introspection_queries, |
461 | | }; |
462 | | |
463 | | trait DispatchableObject: vk::Handle + Copy { |
464 | | type DispatchKey: From<usize>; |
465 | | |
466 | 7.21k | fn get_dispatch_key(&self) -> Self::DispatchKey { |
467 | 7.21k | assert_eq!( |
468 | 7.21k | std::mem::size_of::<Self>(), |
469 | 7.21k | std::mem::size_of::<*const *const c_void>() |
470 | 7.21k | ); |
471 | | // Safe, because all dispatchable objects can be cast to void **. See details at |
472 | | // https://github.com/KhronosGroup/Vulkan-Loader/blob/35b005a5792f6e4c2931d62a37324923f1a71c79/docs/LoaderDriverInterface.md#driver-dispatchable-object-creation. |
473 | 7.21k | let key = unsafe { |
474 | 7.21k | // We use transmute instead of Handle::as_raw here to avoid integer to pointer cast, and |
475 | 7.21k | // allow the miri tests with tree borrows to work with this test. See |
476 | 7.21k | // https://github.com/ash-rs/ash/issues/996 for details. |
477 | 7.21k | let dispatch_table_ptr = std::mem::transmute_copy::<Self, *const *const c_void>(self); |
478 | 7.21k | std::ptr::read(dispatch_table_ptr) |
479 | 7.21k | }; |
480 | 7.21k | (key as usize).into() |
481 | 7.21k | } |
482 | | } |
483 | | |
484 | | #[repr(transparent)] |
485 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
486 | | struct InstanceDispatchKey(usize); |
487 | | |
488 | | impl From<usize> for InstanceDispatchKey { |
489 | 1.92k | fn from(value: usize) -> Self { |
490 | 1.92k | Self(value) |
491 | 1.92k | } |
492 | | } |
493 | | |
494 | | impl DispatchableObject for vk::Instance { |
495 | | type DispatchKey = InstanceDispatchKey; |
496 | | } |
497 | | |
498 | | impl DispatchableObject for vk::PhysicalDevice { |
499 | | type DispatchKey = InstanceDispatchKey; |
500 | | } |
501 | | |
502 | | #[repr(transparent)] |
503 | | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
504 | | struct DeviceDispatchKey(usize); |
505 | | |
506 | | impl From<usize> for DeviceDispatchKey { |
507 | 5.29k | fn from(value: usize) -> Self { |
508 | 5.29k | Self(value) |
509 | 5.29k | } |
510 | | } |
511 | | |
512 | | impl DispatchableObject for vk::Device { |
513 | | type DispatchKey = DeviceDispatchKey; |
514 | | } |
515 | | |
516 | | impl DispatchableObject for vk::CommandBuffer { |
517 | | type DispatchKey = DeviceDispatchKey; |
518 | | } |
519 | | |
520 | | impl DispatchableObject for vk::Queue { |
521 | | type DispatchKey = DeviceDispatchKey; |
522 | | } |
523 | | |
524 | | struct InstanceInfoWrapper<T: Layer> { |
525 | | get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, |
526 | | dispatch_table: InstanceDispatchTable, |
527 | | api_version: ApiVersion, |
528 | | enabled_extensions: BTreeSet<Extension>, |
529 | | // instance_commands and device_commands are recalculated on every vkCreateInstance, so that |
530 | | // the layer can decide which commands to intercept dynamically. |
531 | | instance_commands: Box<[VulkanCommand]>, |
532 | | device_commands: Box<[VulkanCommand]>, |
533 | | is_create_device_hooked: bool, |
534 | | customized_info: T::InstanceInfoContainer, |
535 | | } |
536 | | |
537 | | struct PhysicalDeviceInfoWrapper { |
538 | | owner_instance: vk::Instance, |
539 | | properties: vk::PhysicalDeviceProperties, |
540 | | } |
541 | | |
542 | | struct DeviceInfoWrapper<T: Layer> { |
543 | | dispatch_table: DeviceDispatchTable, |
544 | | get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr, |
545 | | api_version: ApiVersion, |
546 | | enabled_extensions: BTreeSet<Extension>, |
547 | | // device_commands are recalculated on every vkCreateDevice, so that the layer can decide |
548 | | // which commands to intercept dynamically. |
549 | | device_commands: Box<[VulkanCommand]>, |
550 | | customized_info: T::DeviceInfoContainer, |
551 | | } |
552 | | |
553 | | /// A struct that implements all necessarily functions for a layer given a type that implements |
554 | | /// [`Layer`]. |
555 | | /// |
556 | | /// The layer implementation should use [`Global::default`] to construct a [`Global`] object. |
557 | | /// When all `VkInstance`s and `VkDevice`s are destroyed, the drop of [`Global`] is no-op if the |
558 | | /// drop of `T` is no-op under such circumstances. See [the][Layer#initialization] |
559 | | /// [document][Layer#global-clean-up] of [`Layer`] for details on global initialization and |
560 | | /// clean-up. |
561 | | /// |
562 | | /// This is supposed to be a global singleton for a layer to store all necessary data to implement a |
563 | | /// Vulkan layer including dispatch tables, maps between the Vulkan objects and their wrappers, etc. |
564 | | pub struct Global<T: Layer> { |
565 | | instance_map: Mutex<LazyCollection<BTreeMap<InstanceDispatchKey, Arc<InstanceInfoWrapper<T>>>>>, |
566 | | physical_device_map: |
567 | | Mutex<LazyCollection<BTreeMap<vk::PhysicalDevice, Arc<PhysicalDeviceInfoWrapper>>>>, |
568 | | device_map: Mutex<LazyCollection<BTreeMap<DeviceDispatchKey, Arc<DeviceInfoWrapper<T>>>>>, |
569 | | /// Access to the underlying `T`. |
570 | | // layer_info can't be lazily constructed when the first VkInstance is created, because we want |
571 | | // to guarantee that T::default is only called once during the lifetime of Global, so that the |
572 | | // client can put global state in T, and can perform global initialization(e.g. the global |
573 | | // logger) in T::default. If the client needs Global to be trivially destructible, the client |
574 | | // must guarantee that T is also trivially destructible. |
575 | | pub layer_info: T, |
576 | | get_instance_addr_proc_hooked: bool, |
577 | | } |
578 | | |
579 | | impl<T: Layer> Global<T> { |
580 | 7.54k | fn instance() -> impl std::ops::Deref<Target = Self> + 'static { |
581 | 7.54k | T::global_instance() |
582 | 7.54k | } |
583 | | |
584 | 1.82k | fn get_instance_info( |
585 | 1.82k | &self, |
586 | 1.82k | instance: impl DispatchableObject<DispatchKey = InstanceDispatchKey>, |
587 | 1.82k | ) -> Option<Arc<InstanceInfoWrapper<T>>> { |
588 | 1.82k | self.instance_map |
589 | 1.82k | .lock() |
590 | 1.82k | .unwrap() |
591 | 1.82k | .get() |
592 | 1.82k | .get(&instance.get_dispatch_key()) |
593 | 1.82k | .map(Arc::clone) |
594 | 1.82k | } |
595 | | |
596 | 27 | fn create_physical_device_infos<U: Borrow<vk::PhysicalDevice>>( |
597 | 27 | &self, |
598 | 27 | instance: vk::Instance, |
599 | 27 | physical_devices: impl IntoIterator<Item = U>, |
600 | 27 | ) { |
601 | 54 | for physical_device27 in physical_devices { |
602 | 27 | let mut physical_device_map = self.physical_device_map.lock().unwrap(); |
603 | 27 | let mut physical_device_map = physical_device_map.get_mut_or_default(); |
604 | 27 | if let Some(physical_device_info2 ) = physical_device_map.get(physical_device.borrow()) { |
605 | 2 | assert_eq!(physical_device_info.owner_instance, instance); |
606 | 2 | continue; |
607 | 25 | } |
608 | 25 | let instance_info = self.get_instance_info(instance).unwrap_or_else(|| { |
609 | 0 | panic!("Unknown VkInstance handle: {:#018x}", instance.as_raw()) |
610 | 25 | }); |
611 | 25 | let properties = unsafe { |
612 | 25 | instance_info |
613 | 25 | .dispatch_table |
614 | 25 | .core |
615 | 25 | .get_physical_device_properties(*physical_device.borrow()) |
616 | 25 | }; |
617 | 25 | physical_device_map.insert( |
618 | 25 | *physical_device.borrow(), |
619 | 25 | Arc::new(PhysicalDeviceInfoWrapper { |
620 | 25 | owner_instance: instance, |
621 | 25 | properties, |
622 | 25 | }), |
623 | 25 | ); |
624 | 25 | } |
625 | 27 | } |
626 | | |
627 | 5.22k | fn get_device_info( |
628 | 5.22k | &self, |
629 | 5.22k | device: impl DispatchableObject<DispatchKey = DeviceDispatchKey>, |
630 | 5.22k | ) -> Option<Arc<DeviceInfoWrapper<T>>> { |
631 | 5.22k | self.device_map |
632 | 5.22k | .lock() |
633 | 5.22k | .unwrap() |
634 | 5.22k | .get() |
635 | 5.22k | .get(&device.get_dispatch_key()) |
636 | 5.22k | .map(Arc::clone) |
637 | 5.22k | } |
638 | | |
639 | 27 | fn get_physical_info( |
640 | 27 | &self, |
641 | 27 | physical_device: vk::PhysicalDevice, |
642 | 27 | ) -> Option<Arc<PhysicalDeviceInfoWrapper>> { |
643 | 27 | self.physical_device_map |
644 | 27 | .lock() |
645 | 27 | .unwrap() |
646 | 27 | .get() |
647 | 27 | .get(&physical_device) |
648 | 27 | .map(Arc::clone) |
649 | 27 | } |
650 | | |
651 | 48 | extern "system" fn create_instance( |
652 | 48 | create_info: *const vk::InstanceCreateInfo, |
653 | 48 | allocator: *const vk::AllocationCallbacks, |
654 | 48 | p_instance: *mut vk::Instance, |
655 | 48 | ) -> vk::Result { |
656 | 48 | let p_next_chain = unsafe { create_info.as_ref() } |
657 | 48 | .map(|create_info| create_info.p_next as *mut vk::BaseOutStructure) |
658 | 48 | .unwrap_or(null_mut()); |
659 | 48 | let mut p_next_chain: VulkanBaseOutStructChain = unsafe { p_next_chain.as_mut() }.into(); |
660 | 48 | let layer_create_info = p_next_chain.find_map(|out_struct| { |
661 | 48 | let out_struct = out_struct as *mut vk::BaseOutStructure; |
662 | 48 | let layer_create_info = unsafe { |
663 | 48 | ash::match_out_struct!(match out_struct { |
664 | | out_struct @ VkLayerInstanceCreateInfo => { |
665 | 48 | out_struct |
666 | | } |
667 | | _ => { |
668 | 0 | return None; |
669 | | } |
670 | | }) |
671 | | }; |
672 | 48 | if layer_create_info.function == VkLayerFunction::VK_LAYER_LINK_INFO { |
673 | 48 | Some(layer_create_info) |
674 | | } else { |
675 | 0 | None |
676 | | } |
677 | 48 | }); |
678 | 48 | let layer_create_info = match layer_create_info { |
679 | 48 | Some(layer_create_info) => layer_create_info, |
680 | | None => { |
681 | 0 | error!("failed to find the VkLayerInstanceCreateInfo struct in the chain."); |
682 | 0 | return vk::Result::ERROR_INITIALIZATION_FAILED; |
683 | | } |
684 | | }; |
685 | 48 | let layer_info = unsafe { layer_create_info.u.pLayerInfo.as_ref() }.unwrap(); |
686 | 48 | layer_create_info.u.pLayerInfo = layer_info.pNext; |
687 | 48 | |
688 | 48 | let get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr = |
689 | 48 | layer_info.pfnNextGetInstanceProcAddr; |
690 | 48 | |
691 | 48 | let global = Self::instance(); |
692 | 48 | let hooked = |
693 | 48 | T::GlobalHooksInfo::hooked_commands().contains(&LayerVulkanCommand::CreateInstance); |
694 | 48 | let layer_result = if hooked { |
695 | 3 | global.layer_info.global_hooks().create_instance( |
696 | 3 | unsafe { create_info.as_ref() }.unwrap(), |
697 | 3 | layer_info, |
698 | 3 | unsafe { allocator.as_ref() }, |
699 | 3 | p_instance, |
700 | 3 | ) |
701 | | } else { |
702 | 45 | LayerResult::Unhandled |
703 | | }; |
704 | | |
705 | 0 | match layer_result { |
706 | 0 | LayerResult::Handled(Ok(())) => {} |
707 | 0 | LayerResult::Handled(Err(e)) => return e, |
708 | | LayerResult::Unhandled => { |
709 | 144 | let get_proc_addr48 = |name: &CStr| -> *const c_void { |
710 | 144 | let fp = unsafe { get_instance_proc_addr(vk::Instance::null(), name.as_ptr()) }; |
711 | 144 | if let Some(fp56 ) = fp { |
712 | 56 | return fp as *const _; |
713 | 88 | } |
714 | 88 | std::ptr::null() |
715 | 144 | }; |
716 | 48 | let entry = vk::EntryFnV1_0::load(get_proc_addr); |
717 | 48 | |
718 | 48 | let ret: vk::Result = |
719 | 48 | unsafe { (entry.create_instance)(create_info, allocator, p_instance) }; |
720 | 48 | if !matches!0 (ret, vk::Result::SUCCESS) { |
721 | 0 | return ret; |
722 | 48 | } |
723 | | } |
724 | | }; |
725 | | |
726 | 48 | let instance = *unsafe { p_instance.as_ref() }.unwrap(); |
727 | 48 | let ash_instance = unsafe { |
728 | 48 | ash::Instance::load( |
729 | 48 | &ash::vk::StaticFn { |
730 | 48 | get_instance_proc_addr, |
731 | 48 | }, |
732 | 48 | instance, |
733 | 48 | ) |
734 | 48 | }; |
735 | 48 | |
736 | 48 | let create_info = unsafe { create_info.as_ref() }.unwrap(); |
737 | 48 | let enabled_extensions = unsafe { |
738 | 48 | slice_from_raw_parts( |
739 | 48 | create_info.pp_enabled_extension_names, |
740 | 48 | create_info.enabled_extension_count, |
741 | 48 | ) |
742 | 48 | }; |
743 | 48 | let enabled_extensions = unsafe { slice_to_owned_strings(enabled_extensions) } |
744 | 48 | .filter_map(|extension_name| match extension_name.as_str().try_into()8 { |
745 | 8 | Ok(extension) => Some(extension), |
746 | 0 | Err(e) => { |
747 | 0 | warn!( |
748 | 0 | "Failed to recognize the extension {}: {}", |
749 | | extension_name, e |
750 | | ); |
751 | 0 | None |
752 | | } |
753 | 48 | }8 ) |
754 | 48 | .collect(); |
755 | 48 | let api_version = unsafe { create_info.p_application_info.as_ref() } |
756 | 48 | .map(|app_info| app_info.api_version8 ) |
757 | 48 | .unwrap_or(0); |
758 | 48 | let api_version = if api_version == 0 { |
759 | 42 | ApiVersion { major: 1, minor: 0 } |
760 | | } else { |
761 | 6 | api_version.into() |
762 | | }; |
763 | 48 | let ash_instance = Arc::new(ash_instance); |
764 | 48 | let customized_info = global.layer_info.create_instance_info( |
765 | 48 | create_info, |
766 | 48 | unsafe { allocator.as_ref() }, |
767 | 48 | Arc::clone(&ash_instance), |
768 | 48 | get_instance_proc_addr, |
769 | 48 | ); |
770 | 48 | let is_create_device_hooked = global |
771 | 48 | .layer_info |
772 | 48 | .hooked_instance_commands(customized_info.borrow()) |
773 | 48 | .any(|command| command == LayerVulkanCommand::CreateDevice8 ); |
774 | 48 | let instance_commands = global.create_instance_commands(customized_info.borrow()); |
775 | 48 | let device_commands = global.create_device_commands(customized_info.borrow(), None); |
776 | 48 | { |
777 | 48 | let key = instance.get_dispatch_key(); |
778 | 48 | let mut instance_map = global.instance_map.lock().unwrap(); |
779 | 48 | let mut instance_map = instance_map.get_mut_or_default(); |
780 | 48 | if instance_map.contains_key(&key) { |
781 | 0 | error!( |
782 | 0 | "duplicate instances: instance {:?} already exists", |
783 | | instance |
784 | | ); |
785 | 0 | return vk::Result::ERROR_INITIALIZATION_FAILED; |
786 | 48 | } |
787 | 48 | instance_map.insert( |
788 | 48 | key, |
789 | 48 | Arc::new(InstanceInfoWrapper { |
790 | 48 | get_instance_proc_addr, |
791 | 48 | dispatch_table: InstanceDispatchTable::load( |
792 | 48 | get_instance_proc_addr, |
793 | 48 | ash_instance, |
794 | 48 | ), |
795 | 48 | api_version, |
796 | 48 | enabled_extensions, |
797 | 48 | instance_commands, |
798 | 48 | device_commands, |
799 | 48 | is_create_device_hooked, |
800 | 48 | customized_info, |
801 | 48 | }), |
802 | 48 | ); |
803 | 48 | } |
804 | 48 | vk::Result::SUCCESS |
805 | 48 | } |
806 | | |
807 | 49 | extern "system" fn destroy_instance( |
808 | 49 | instance: vk::Instance, |
809 | 49 | allocator: *const vk::AllocationCallbacks, |
810 | 49 | ) { |
811 | 49 | if instance == vk::Instance::null() { |
812 | 1 | return; |
813 | 48 | } |
814 | 48 | let dispatch_key = instance.get_dispatch_key(); |
815 | 48 | let global = Self::instance(); |
816 | 48 | let instance_info = global |
817 | 48 | .instance_map |
818 | 48 | .lock() |
819 | 48 | .unwrap() |
820 | 48 | .get_mut_or_default() |
821 | 48 | .remove(&dispatch_key); |
822 | 48 | let instance_info = instance_info.unwrap(); |
823 | 48 | let instance_info = match Arc::try_unwrap(instance_info) { |
824 | 48 | Ok(instance_info) => instance_info, |
825 | 0 | Err(_) => panic!( |
826 | 0 | "This should be the sole instance dispatch table reference for instance {:?}.", |
827 | 0 | instance |
828 | 0 | ), |
829 | | }; |
830 | 48 | global |
831 | 48 | .physical_device_map |
832 | 48 | .lock() |
833 | 48 | .unwrap() |
834 | 48 | .get_mut_or_default() |
835 | 48 | .retain(|_, physical_device_info| physical_device_info.owner_instance != instance25 ); |
836 | 48 | unsafe { |
837 | 48 | (instance_info.dispatch_table.core.fp_v1_0().destroy_instance)(instance, allocator) |
838 | | }; |
839 | 49 | } |
840 | | |
841 | 54 | extern "system" fn enumerate_physical_devices( |
842 | 54 | instance: vk::Instance, |
843 | 54 | p_physical_device_count: *mut u32, |
844 | 54 | p_physical_devices: *mut vk::PhysicalDevice, |
845 | 54 | ) -> vk::Result { |
846 | 54 | let global = Self::instance(); |
847 | 54 | let ash_instance = &global |
848 | 54 | .get_instance_info(instance) |
849 | 54 | .expect("Must be a valid VkInstance") |
850 | 54 | .dispatch_table |
851 | 54 | .core; |
852 | 54 | let res = unsafe { |
853 | 54 | (ash_instance.fp_v1_0().enumerate_physical_devices)( |
854 | 54 | instance, |
855 | 54 | p_physical_device_count, |
856 | 54 | p_physical_devices, |
857 | 54 | ) |
858 | 54 | }; |
859 | 54 | if (res == vk::Result::SUCCESS || res == vk::Result::INCOMPLETE0 ) |
860 | 54 | && !p_physical_devices.is_null() |
861 | 27 | { |
862 | 27 | let physical_device_count = unsafe { p_physical_device_count.as_ref() }.unwrap(); |
863 | 27 | let physical_devices = |
864 | 27 | unsafe { slice_from_raw_parts(p_physical_devices, *physical_device_count) }; |
865 | 27 | global.create_physical_device_infos(instance, physical_devices); |
866 | 27 | } |
867 | 54 | res |
868 | 54 | } |
869 | | |
870 | 0 | extern "system" fn enumerate_physical_device_groups( |
871 | 0 | instance: vk::Instance, |
872 | 0 | p_physical_device_group_count: *mut u32, |
873 | 0 | p_physical_device_group_properties: *mut vk::PhysicalDeviceGroupProperties, |
874 | 0 | ) -> vk::Result { |
875 | 0 | let global = Self::instance(); |
876 | 0 | let ash_instance = &global |
877 | 0 | .get_instance_info(instance) |
878 | 0 | .expect("Must be a valid VkInstance") |
879 | 0 | .dispatch_table |
880 | 0 | .core; |
881 | 0 | let res = unsafe { |
882 | 0 | (ash_instance.fp_v1_1().enumerate_physical_device_groups)( |
883 | 0 | instance, |
884 | 0 | p_physical_device_group_count, |
885 | 0 | p_physical_device_group_properties, |
886 | 0 | ) |
887 | 0 | }; |
888 | 0 | if (res == vk::Result::SUCCESS || res == vk::Result::INCOMPLETE) |
889 | 0 | && !p_physical_device_group_count.is_null() |
890 | 0 | { |
891 | 0 | let physical_device_group_count = |
892 | 0 | *unsafe { p_physical_device_group_count.as_ref() }.unwrap(); |
893 | 0 | let physical_device_groups = unsafe { |
894 | 0 | slice_from_raw_parts( |
895 | 0 | p_physical_device_group_properties, |
896 | 0 | physical_device_group_count, |
897 | 0 | ) |
898 | 0 | }; |
899 | 0 | let physical_devices = |
900 | 0 | physical_device_groups |
901 | 0 | .iter() |
902 | 0 | .flat_map(|physical_device_group| { |
903 | 0 | &physical_device_group.physical_devices[..physical_device_group |
904 | 0 | .physical_device_count |
905 | 0 | .try_into() |
906 | 0 | .unwrap()] |
907 | 0 | }); |
908 | 0 | global.create_physical_device_infos(instance, physical_devices); |
909 | 0 | } |
910 | 0 | res |
911 | 0 | } |
912 | | |
913 | 25 | extern "system" fn create_device( |
914 | 25 | physical_device: vk::PhysicalDevice, |
915 | 25 | create_info: *const vk::DeviceCreateInfo, |
916 | 25 | p_allocator: *const vk::AllocationCallbacks, |
917 | 25 | p_device: *mut vk::Device, |
918 | 25 | ) -> vk::Result { |
919 | 25 | let create_info = unsafe { create_info.as_ref() }.unwrap(); |
920 | 25 | let mut p_next_chain: VulkanBaseOutStructChain = |
921 | 25 | unsafe { (create_info.p_next as *mut vk::BaseOutStructure).as_mut() }.into(); |
922 | 25 | let layer_create_info = p_next_chain.find_map(|out_struct| { |
923 | 25 | let out_struct = out_struct as *mut vk::BaseOutStructure; |
924 | 25 | let layer_create_info = unsafe { |
925 | 25 | ash::match_out_struct!(match out_struct { |
926 | | out_struct @ VkLayerDeviceCreateInfo => { |
927 | 25 | out_struct |
928 | | } |
929 | | _ => { |
930 | 0 | return None; |
931 | | } |
932 | | }) |
933 | | }; |
934 | 25 | if layer_create_info.function == VkLayerFunction::VK_LAYER_LINK_INFO { |
935 | 25 | Some(layer_create_info) |
936 | | } else { |
937 | 0 | None |
938 | | } |
939 | 25 | }); |
940 | 25 | let layer_create_info = match layer_create_info { |
941 | 25 | Some(layer_create_info) => layer_create_info, |
942 | | None => { |
943 | 0 | error!("failed to find the VkLayerDeviceCreateInfo struct in the chain."); |
944 | 0 | return vk::Result::ERROR_INITIALIZATION_FAILED; |
945 | | } |
946 | | }; |
947 | 25 | let layer_link = unsafe { layer_create_info.u.pLayerInfo.as_ref() }.unwrap(); |
948 | 25 | layer_create_info.u.pLayerInfo = layer_link.pNext; |
949 | 25 | |
950 | 25 | let get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr = |
951 | 25 | layer_link.pfnNextGetInstanceProcAddr; |
952 | 25 | let get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr = layer_link.pfnNextGetDeviceProcAddr; |
953 | 25 | |
954 | 25 | let global = Self::instance(); |
955 | 25 | let physical_device_info = global |
956 | 25 | .get_physical_info(physical_device) |
957 | 25 | .expect("physical device must be a valid VkPhysicalDevice."); |
958 | 25 | let instance_info = global |
959 | 25 | .get_instance_info(physical_device_info.owner_instance) |
960 | 25 | .expect("The owner instance of this physical device must be registered."); |
961 | 25 | |
962 | 25 | let create_device_name = CString::new("vkCreateDevice").unwrap(); |
963 | 25 | let next_create_device = unsafe { |
964 | 25 | get_instance_proc_addr( |
965 | 25 | instance_info.dispatch_table.core.handle(), |
966 | 25 | create_device_name.as_ptr(), |
967 | 25 | ) |
968 | 25 | }; |
969 | 25 | let next_create_device: vk::PFN_vkCreateDevice = |
970 | 25 | unsafe { std::mem::transmute(next_create_device) }; |
971 | 25 | let mut create_info = *create_info; |
972 | 25 | let requested_extensions = unsafe { |
973 | 25 | slice_from_raw_parts( |
974 | 25 | create_info.pp_enabled_extension_names, |
975 | 25 | create_info.enabled_extension_count, |
976 | 25 | ) |
977 | 25 | }; |
978 | 25 | let requested_extensions = |
979 | 25 | unsafe { slice_to_owned_strings(requested_extensions) }.collect::<Vec<_>>(); |
980 | 25 | let create_device_res = if instance_info.is_create_device_hooked { |
981 | 4 | instance_info |
982 | 4 | .customized_info |
983 | 4 | .borrow() |
984 | 4 | .hooks() |
985 | 4 | .create_device( |
986 | 4 | physical_device, |
987 | 4 | &create_info, |
988 | 4 | layer_link, |
989 | 4 | unsafe { p_allocator.as_ref() }, |
990 | 4 | unsafe { ptr_as_uninit_mut(p_device) }.unwrap(), |
991 | 4 | ) |
992 | | } else { |
993 | 21 | LayerResult::Unhandled |
994 | | }; |
995 | 25 | let layer_manifest = T::manifest(); |
996 | 0 | match create_device_res { |
997 | 0 | LayerResult::Handled(Ok(())) => {} |
998 | 0 | LayerResult::Handled(Err(e)) => return e, |
999 | | LayerResult::Unhandled => { |
1000 | 25 | let enabled_extensions = requested_extensions |
1001 | 25 | .iter() |
1002 | 25 | .filter_map(|extension_name| { |
1003 | 12 | let extension_name_cstring = CString::new(extension_name.clone()) |
1004 | 12 | .unwrap_or_else(|e| { |
1005 | 0 | panic!("Failed to create CString from {}: {}", extension_name, e) |
1006 | 12 | }); |
1007 | 12 | let extension: Extension11 = match extension_name.as_str().try_into() { |
1008 | 11 | Ok(extension) => extension, |
1009 | 1 | Err(_) => return Some(extension_name_cstring), |
1010 | | }; |
1011 | 11 | if layer_manifest |
1012 | 11 | .device_extensions |
1013 | 11 | .iter() |
1014 | 11 | .any(|layer_extension| layer_extension.name == extension9 ) |
1015 | | { |
1016 | 7 | None |
1017 | | } else { |
1018 | 4 | Some(extension_name_cstring) |
1019 | | } |
1020 | 25 | }12 ) |
1021 | 25 | .collect::<Vec<_>>(); |
1022 | 25 | let enabled_extensions = enabled_extensions |
1023 | 25 | .iter() |
1024 | 25 | .map(|extension_name| extension_name.as_ptr()5 ) |
1025 | 25 | .collect::<Vec<_>>(); |
1026 | 25 | create_info.enabled_extension_count = enabled_extensions.len().try_into().unwrap(); |
1027 | 25 | create_info.pp_enabled_extension_names = if enabled_extensions.is_empty() { |
1028 | 20 | null() |
1029 | | } else { |
1030 | 5 | enabled_extensions.as_ptr() |
1031 | | }; |
1032 | 25 | let res = unsafe { |
1033 | 25 | next_create_device(physical_device, &create_info, p_allocator, p_device) |
1034 | 25 | }; |
1035 | 25 | if res != vk::Result::SUCCESS { |
1036 | 2 | return res; |
1037 | 23 | } |
1038 | | } |
1039 | | } |
1040 | | |
1041 | 23 | let device = *unsafe { p_device.as_ref() }.unwrap(); |
1042 | 23 | let ash_instance = Arc::clone(&instance_info.dispatch_table.core); |
1043 | 23 | let ash_device = unsafe { |
1044 | 23 | // ash will also try to load instance-level dispatchable commands with |
1045 | 23 | // vkGetDeviceProcAddr. Although that won't end up with undefined behavior, and we |
1046 | 23 | // should always expect NULL returned according to the spec, we may see Android vulkan |
1047 | 23 | // loader complaining about internal vkGetDeviceProcAddr called for <function name>, |
1048 | 23 | // which should be benign. |
1049 | 23 | ash::Device::load( |
1050 | 23 | &vk::InstanceFnV1_0 { |
1051 | 23 | get_device_proc_addr, |
1052 | 23 | ..ash_instance.fp_v1_0().clone() |
1053 | 23 | }, |
1054 | 23 | device, |
1055 | 23 | ) |
1056 | 23 | }; |
1057 | 23 | let ash_device = Arc::new(ash_device); |
1058 | 23 | let enabled_extensions = requested_extensions |
1059 | 23 | .iter() |
1060 | 23 | .filter_map(|name| { |
1061 | 9 | let extension_name: Option<Extension> = name.as_str().try_into().ok(); |
1062 | 9 | extension_name |
1063 | 23 | }) |
1064 | 23 | .collect::<BTreeSet<_>>(); |
1065 | 23 | let api_version = instance_info |
1066 | 23 | .api_version |
1067 | 23 | .min(physical_device_info.properties.api_version.into()); |
1068 | 23 | let customized_info = global.layer_info.create_device_info( |
1069 | 23 | physical_device, |
1070 | 23 | &create_info, |
1071 | 23 | unsafe { p_allocator.as_ref() }, |
1072 | 23 | Arc::clone(&ash_device), |
1073 | 23 | get_device_proc_addr, |
1074 | 23 | ); |
1075 | 23 | let device_commands = global.create_device_commands( |
1076 | 23 | instance_info.customized_info.borrow(), |
1077 | 23 | Some(customized_info.borrow()), |
1078 | 23 | ); |
1079 | 23 | { |
1080 | 23 | let mut device_map = global.device_map.lock().unwrap(); |
1081 | 23 | let mut device_map = device_map.get_mut_or_default(); |
1082 | 23 | assert!( |
1083 | 23 | !device_map.contains_key(&device.get_dispatch_key()), |
1084 | 0 | "duplicate VkDevice: {:?}", |
1085 | | device |
1086 | | ); |
1087 | 23 | device_map.insert( |
1088 | 23 | device.get_dispatch_key(), |
1089 | 23 | Arc::new(DeviceInfoWrapper { |
1090 | 23 | dispatch_table: DeviceDispatchTable::load(get_device_proc_addr, ash_device), |
1091 | 23 | get_device_proc_addr, |
1092 | 23 | api_version, |
1093 | 23 | enabled_extensions, |
1094 | 23 | device_commands, |
1095 | 23 | customized_info, |
1096 | 23 | }), |
1097 | 23 | ); |
1098 | 23 | } |
1099 | 23 | vk::Result::SUCCESS |
1100 | 25 | } |
1101 | | |
1102 | 24 | extern "system" fn destroy_device( |
1103 | 24 | device: vk::Device, |
1104 | 24 | p_allocator: *const vk::AllocationCallbacks, |
1105 | 24 | ) { |
1106 | 24 | if device == vk::Device::null() { |
1107 | 1 | return; |
1108 | 23 | } |
1109 | 23 | let global = Self::instance(); |
1110 | 23 | let device_info = match Arc::try_unwrap( |
1111 | 23 | global |
1112 | 23 | .device_map |
1113 | 23 | .lock() |
1114 | 23 | .unwrap() |
1115 | 23 | .get_mut_or_default() |
1116 | 23 | .remove(&device.get_dispatch_key()) |
1117 | 23 | .expect("device must be registered"), |
1118 | 23 | ) { |
1119 | 23 | Ok(device_info) => device_info, |
1120 | 0 | Err(_) => panic!("This should be the sole owner of the device {:?}", device), |
1121 | | }; |
1122 | 23 | let allocation_callback = unsafe { p_allocator.as_ref() }; |
1123 | 23 | unsafe { |
1124 | 23 | device_info |
1125 | 23 | .dispatch_table |
1126 | 23 | .core |
1127 | 23 | .destroy_device(allocation_callback) |
1128 | | }; |
1129 | 24 | } |
1130 | | |
1131 | 4 | fn layer_properties() -> Vec<vk::LayerProperties> { |
1132 | 4 | let layer_manifest = T::manifest(); |
1133 | 4 | // layer_name and description will be set later |
1134 | 4 | let mut layer_property: vk::LayerProperties = vk::LayerProperties::builder() |
1135 | 4 | .spec_version(layer_manifest.spec_version) |
1136 | 4 | .implementation_version(layer_manifest.implementation_version) |
1137 | 4 | .build(); |
1138 | 4 | assert!(layer_manifest.name.len() < vk::MAX_EXTENSION_NAME_SIZE); |
1139 | 4 | let layer_name = CString::new(layer_manifest.name).unwrap(); |
1140 | 4 | let layer_name = cast_slice(layer_name.as_bytes()); |
1141 | 4 | layer_property.layer_name[..layer_name.len()].copy_from_slice(layer_name); |
1142 | 4 | |
1143 | 4 | let layer_description = CString::new(layer_manifest.description).unwrap(); |
1144 | 4 | let layer_description = cast_slice(layer_description.as_bytes()); |
1145 | 4 | assert!(layer_description.len() < vk::MAX_DESCRIPTION_SIZE); |
1146 | 4 | layer_property.description[..layer_description.len()].copy_from_slice(layer_description); |
1147 | 4 | vec![layer_property] |
1148 | 4 | } |
1149 | | |
1150 | | // Introspection queries required by Android, so we need to expose them as public functions so |
1151 | | // that the user can further expose them as functions exported by the dynamic link library. |
1152 | | |
1153 | | /// The `vkEnumerateInstanceLayerProperties` entry point provided by the layer framework. |
1154 | | /// |
1155 | | /// The return value is decided by [`Layer::manifest`]. Only enumerate the layer itself |
1156 | | /// according to the |
1157 | | /// [layer rule](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateInstanceLayerProperties%20must%20enumerate%20and%20only%20enumerate%20the%20layer%20itself.>). |
1158 | | /// |
1159 | | /// # Safety |
1160 | | /// See valid usage of `vkEnumerateInstanceLayerProperties` at |
1161 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html>. |
1162 | | #[deny(unsafe_op_in_unsafe_fn)] |
1163 | 4 | pub unsafe extern "system" fn enumerate_instance_layer_properties( |
1164 | 4 | property_count: *mut u32, |
1165 | 4 | properties: *mut vk::LayerProperties, |
1166 | 4 | ) -> vk::Result { |
1167 | 4 | let ret_properties = Self::layer_properties(); |
1168 | 4 | // Safe, because the caller guarantees that `property_count` is a valid pointer to u32, and |
1169 | 4 | // if the value referenced by property_count is not 0, and properties is not NULL, |
1170 | 4 | // properties must be a valid pointer to an array of property_count vk::LayerProperties |
1171 | 4 | // structures. See details in |
1172 | 4 | // VUID-vkEnumerateInstanceLayerProperties-pPropertyCount-parameter and |
1173 | 4 | // VUID-vkEnumerateInstanceLayerProperties-pProperties-parameter. |
1174 | 4 | unsafe { |
1175 | 4 | fill_vk_out_array( |
1176 | 4 | &ret_properties, |
1177 | 4 | NonNull::new(property_count).unwrap(), |
1178 | 4 | properties, |
1179 | 4 | ) |
1180 | 4 | } |
1181 | 4 | } |
1182 | | |
1183 | | /// The `vkEnumerateInstanceExtensionProperties` entry point provided by the layer framework. |
1184 | | /// |
1185 | | /// Returns an empty list with `VK_SUCCESS` if the layer name matches; returns |
1186 | | /// `VK_ERROR_LAYER_NOT_PRESENT` with all the out pointers untouched, according to the |
1187 | | /// [`LLP_LAYER_15`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=LLP_LAYER_15,Conventions%20and%20Rules>) |
1188 | | /// rule. |
1189 | | /// |
1190 | | /// # Safety |
1191 | | /// See valid usage of `vkEnumerateInstanceExtensionProperties` at |
1192 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>. |
1193 | | #[deny(unsafe_op_in_unsafe_fn)] |
1194 | 2 | pub unsafe extern "system" fn enumerate_instance_extension_properties( |
1195 | 2 | layer_name: *const c_char, |
1196 | 2 | property_count: *mut u32, |
1197 | 2 | _: *mut vk::ExtensionProperties, |
1198 | 2 | ) -> vk::Result { |
1199 | 2 | if !layer_name.is_null() { |
1200 | 2 | let layer_name = unsafe { CStr::from_ptr(layer_name) } |
1201 | 2 | .to_str() |
1202 | 2 | .expect(concat!( |
1203 | 2 | "According to \ |
1204 | 2 | VUID-vkEnumerateInstanceExtensionProperties-pLayerName-parameter, ", |
1205 | 2 | "if p_layer_name is not NULL, p_layer_name must be a null-terminated UTF-8 \ |
1206 | 2 | string." |
1207 | 2 | )); |
1208 | 2 | if layer_name == T::manifest().name { |
1209 | | // Safe, because the caller guarantees that if the passed in `property_count` is not |
1210 | | // null, it's a valid pointer to u32. |
1211 | 2 | if let Some(property_count) = unsafe { property_count.as_mut() } { |
1212 | 2 | *property_count = 0; |
1213 | 2 | }0 |
1214 | 2 | return vk::Result::SUCCESS; |
1215 | 0 | } |
1216 | 0 | } |
1217 | 0 | vk::Result::ERROR_LAYER_NOT_PRESENT |
1218 | 2 | } |
1219 | | |
1220 | | /// The `vkEnumerateDeviceLayerProperties` entry point provided by the layer framework. |
1221 | | /// |
1222 | | /// The return value is decided by [`Layer::manifest`]. Only enumerate the layer itself |
1223 | | /// according to the |
1224 | | /// [layer interface version 0 requirement](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceLayerProperties%20enumerates%20a,device%20function%20interception.>). |
1225 | | /// Note that although this function |
1226 | | /// [is deprecated](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceLayerProperties%20is%20deprecated,in%20undefined%20behavior.>) |
1227 | | /// in the Khronos Vulkan loader, it is still used in the Android Vulkan loader. |
1228 | | /// |
1229 | | /// # Safety |
1230 | | /// See valid usage of `vkEnumerateDeviceLayerProperties` at |
1231 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateDeviceLayerProperties.html>. |
1232 | | #[deny(unsafe_op_in_unsafe_fn)] |
1233 | 0 | pub unsafe extern "system" fn enumerate_device_layer_properties( |
1234 | 0 | _: vk::PhysicalDevice, |
1235 | 0 | p_property_count: *mut u32, |
1236 | 0 | p_properties: *mut vk::LayerProperties, |
1237 | 0 | ) -> vk::Result { |
1238 | 0 | // TODO: call into the customized instance data with the vk::PhysicalDevice to provide more |
1239 | 0 | // flexibility. |
1240 | 0 | let ret_properties = Self::layer_properties(); |
1241 | 0 | // Safe, because the caller guarantees that `p_property_count` is a valid pointer to u32, |
1242 | 0 | // and if the value referenced by `p_property_count` is not 0, and `p_properties` is not |
1243 | 0 | // NULL, `p_properties` must be a valid pointer to an array of `p_property_count` |
1244 | 0 | // vk::LayerProperties structures. See details in |
1245 | 0 | // VUID-vkEnumerateDeviceLayerProperties-pPropertyCount-parameter |
1246 | 0 | // and VUID-vkEnumerateDeviceLayerProperties-pProperties-parameter. |
1247 | 0 | unsafe { |
1248 | 0 | fill_vk_out_array( |
1249 | 0 | &ret_properties, |
1250 | 0 | NonNull::new(p_property_count).expect(concat!( |
1251 | 0 | "p_property_count must be a valid pointer to u32 according to ", |
1252 | 0 | "VUID-vkEnumerateDeviceLayerProperties-pPropertyCount-parameter" |
1253 | 0 | )), |
1254 | 0 | p_properties, |
1255 | 0 | ) |
1256 | 0 | } |
1257 | 0 | } |
1258 | | |
1259 | | /// The `vkEnumerateDeviceExtensionProperties` entry point provided by the layer framework. |
1260 | | /// |
1261 | | /// The return value is decided by [`Layer::manifest`] where `p_layer_name` is itself. If the |
1262 | | /// `p_layer_name` doesn't match, the behavior depends on whether the `physical_device` argument |
1263 | | /// is `VK_NULL_HANDLE` or not: if `physical_device` argument is `VK_NULL_HANDLE`, |
1264 | | /// `VK_ERROR_LAYER_NOT_PRESENT` is returned; if `physical_device` is not `VK_NULL_HANDLE`, the |
1265 | | /// layer framework calls into the next layer of the call chain. This behavior is defined by the [layer interface version 0](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceExtensionProperties%20enumerates%20device,function%20never%20fails.>) |
1266 | | /// and |
1267 | | /// [`LLP_LAYER_16`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=LLP_LAYER_16,Conventions%20and%20Rules>). |
1268 | | /// |
1269 | | /// # Safety |
1270 | | /// See valid usage of `vkEnumerateDeviceExtensionProperties` at |
1271 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateDeviceExtensionProperties.html>. |
1272 | | /// A NULL `VkPhysicalDevice` can also be used to call this interface, according to the |
1273 | | /// [layer interface version 0](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#:~:text=vkEnumerateDeviceExtensionProperties%20enumerates%20device,function%20never%20fails.>). |
1274 | | #[deny(unsafe_op_in_unsafe_fn)] |
1275 | 8 | pub unsafe extern "system" fn enumerate_device_extension_properties( |
1276 | 8 | physical_device: vk::PhysicalDevice, |
1277 | 8 | p_layer_name: *const c_char, |
1278 | 8 | p_property_count: *mut u32, |
1279 | 8 | p_properties: *mut vk::ExtensionProperties, |
1280 | 8 | ) -> vk::Result { |
1281 | 8 | let layer_manifest = T::manifest(); |
1282 | 8 | let is_this_layer = if p_layer_name.is_null() { |
1283 | 3 | false |
1284 | | } else { |
1285 | 5 | unsafe { CStr::from_ptr(p_layer_name) } |
1286 | 5 | .to_str() |
1287 | 5 | .map(|layer_name| layer_name == layer_manifest.name) |
1288 | 5 | .unwrap_or(false) |
1289 | | }; |
1290 | 8 | if !is_this_layer { |
1291 | 4 | if physical_device == vk::PhysicalDevice::null() { |
1292 | 2 | return vk::Result::ERROR_LAYER_NOT_PRESENT; |
1293 | 2 | } |
1294 | 2 | let global = Self::instance(); |
1295 | 2 | let physical_device_info = global |
1296 | 2 | .get_physical_info(physical_device) |
1297 | 2 | .expect("physical device must be a valid VkPhysicalDevice."); |
1298 | 2 | let instance_info = global |
1299 | 2 | .get_instance_info(physical_device_info.owner_instance) |
1300 | 2 | .expect("The owner instance of this physical device must be registered."); |
1301 | 2 | return unsafe { |
1302 | 2 | (instance_info |
1303 | 2 | .dispatch_table |
1304 | 2 | .core |
1305 | 2 | .fp_v1_0() |
1306 | 2 | .enumerate_device_extension_properties)( |
1307 | 2 | physical_device, |
1308 | 2 | p_layer_name, |
1309 | 2 | p_property_count, |
1310 | 2 | p_properties, |
1311 | 2 | ) |
1312 | | }; |
1313 | 4 | } |
1314 | 4 | let property_count = NonNull::new(p_property_count).expect(concat!( |
1315 | 4 | "`p_property_count` must be a valid pointer to u32 according to ", |
1316 | 4 | "VUID-vkEnumerateDeviceExtensionProperties-pPropertyCount-parameter." |
1317 | 4 | )); |
1318 | 4 | // Safe because the caller guarantees `p_property_count` is a valid pointer to u32 according |
1319 | 4 | // to VUID-vkEnumerateDeviceExtensionProperties-pPropertyCount-parameter, and if |
1320 | 4 | // `p_property_count` doesn't point to 0, p_properties is either NULL or a valid pointer to |
1321 | 4 | // an array of `p_property_count` `vk::ExtensionProperties` structures according to |
1322 | 4 | // VUID-vkEnumerateDeviceExtensionProperties-pProperties-parameter. |
1323 | 4 | let device_extensions = layer_manifest |
1324 | 4 | .device_extensions |
1325 | 4 | .iter() |
1326 | 4 | .cloned() |
1327 | 4 | .map(Into::<vk::ExtensionProperties>::into) |
1328 | 4 | .collect::<Vec<_>>(); |
1329 | 4 | unsafe { fill_vk_out_array(&device_extensions, property_count, p_properties) } |
1330 | 8 | } |
1331 | | |
1332 | | /// The `vkGetInstanceProcAddr` entry point provided by the layer framework. |
1333 | | /// |
1334 | | /// The layer framework will make use of [`Layer::hooked_instance_commands`] and |
1335 | | /// [`Layer::hooked_device_commands`] (with the `device_info` argument being [`None`]) to decide |
1336 | | /// whether a local function pointer should be returned or should just passthrough to the next |
1337 | | /// layer in the call chain. Note that the layer implementation doesn't have to take care of |
1338 | | /// whether the command is enabled/available, and only needs to use the interfaces to express |
1339 | | /// the intent. The layer framework should handle most of the logic(e.g. the requirement of |
1340 | | /// [`LLP_LAYER_18`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#pre-instance-functions:~:text=LLP_LAYER_18,Conventions%20and%20Rules>)) |
1341 | | /// here unless the layer implementation decides to intercept `vkGetInstanceProcAddr`. |
1342 | | /// |
1343 | | /// The actual behavior is defined as follow: |
1344 | | /// |
1345 | | /// 1. If the `instance` parameter is `VK_NULL_HANDLE`, returns the local function pointers of |
1346 | | /// the current layer for all |
1347 | | /// [global commands](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.) |
1348 | | /// and `vkGetInstanceProcAddr`; `NULL` is returned for other Vulkan commands. One culprit |
1349 | | /// for now is that currently the layer framework always returns `NULL` for |
1350 | | /// `vkEnumerateInstanceVersion`, but that should be Ok, because the layer framework |
1351 | | /// currently doesn't allow to intercept pre-instance functions anyway, so this function |
1352 | | /// should be completely handled by the Vulkan loader. |
1353 | | /// |
1354 | | /// 1. If instance is not null, the situation is more complicated: |
1355 | | /// |
1356 | | /// If the layer implementation intercepts the `vkGetInstanceProcAddr` function(i.e. |
1357 | | /// [`Layer::hooked_instance_commands`] returns `vkGetInstanceProcAddr`), the layer framework |
1358 | | /// just calls [`InstanceHooks::get_instance_proc_addr`] and return the return value if |
1359 | | /// [`LayerResult::Handled`] is returned. |
1360 | | /// |
1361 | | /// Otherwise, the layer framework handles most of the logic according to |
1362 | | /// [`Layer::hooked_instance_commands`] and [`Layer::hooked_device_commands`]: |
1363 | | /// |
1364 | | /// 1. For dispatchable commands that are introspection queries: |
1365 | | /// `vkEnumerateDeviceExtensionProperties`, `vkEnumerateDeviceLayerProperties`, always |
1366 | | /// returns a local function pointer of the current layer. |
1367 | | /// |
1368 | | /// 1. For `vkDestroyInstance`, `vkCreateDevice`, `vkDestroyDevice`, always returns a local |
1369 | | /// function pointer. The layer framework needs to intercept those functions to build the |
1370 | | /// dispatch table and perform instance/device specific initialization. Note that |
1371 | | /// `vkCreateInstance` is a |
1372 | | /// [global command](<https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html#:~:text=The%20global%20commands%20are%3A%20vkEnumerateInstanceVersion%2C%20vkEnumerateInstanceExtensionProperties%2C%20vkEnumerateInstanceLayerProperties%2C%20and%20vkCreateInstance.>), |
1373 | | /// so `NULL` will be returned if the `instance` argument is not `VK_NULL_HANDLE`. |
1374 | | /// |
1375 | | /// 1. For `vkEnumeratePhysicalDevices`, `vkEnumeratePhysicalDeviceGroups`, always returns a |
1376 | | /// local function pointer. The layer framework needs to intercept those functions to |
1377 | | /// build a map between `VkPhysicalDevice` and `VkInstance` so that the layer framework |
1378 | | /// can find the correspondent `VkInstance` in `vkCreateDevice`. |
1379 | | /// |
1380 | | /// 1. For other core dispatchable commands and enabled instance extension dispatchable |
1381 | | /// commands, if the layer implementation decides to intercept(according to |
1382 | | /// [`Layer::hooked_instance_commands`] and [`Layer::hooked_device_commands`]), returns a |
1383 | | /// local function pointer, otherwise returns the function pointer from the next layer. |
1384 | | /// |
1385 | | /// 1. For disabled instance extension dispatchable commands(either because the API version |
1386 | | /// is not high enough or the extension is not enabled), directly call to the next layer |
1387 | | /// regardless of whether the layer implementation wants to intercept the command or not, |
1388 | | /// and rely on the next layer to return `NULL`. |
1389 | | /// |
1390 | | /// 1. For device extension dispatchable commands directly supported by the layer |
1391 | | /// implementation according to [`LayerManifest::device_extensions`], always returns a |
1392 | | /// local function pointer if the layer implementation intercepts the command. Otherwise, |
1393 | | /// returns the function pointer of the next layer. |
1394 | | /// |
1395 | | /// 1. For device extension dispatchable commands not directly supported by the layer(i.e. |
1396 | | /// the extension doesn't appear in the [`LayerManifest::device_extensions`] of the |
1397 | | /// current layer): |
1398 | | /// |
1399 | | /// * If the next layer returns `NULL`, `NULL` is always returned regardless of whether |
1400 | | /// the layer implementation wants to intercept the command. This indicates that this |
1401 | | /// device extension dispathcable command is not available for the instance. |
1402 | | /// |
1403 | | /// * if the next layer returns non-NULL, a local function pointer is returned if the |
1404 | | /// layer implementation wants to intercept the command, otherwise returns the function |
1405 | | /// pointer of the next layer This indicates that this device extension dispathcable |
1406 | | /// command is available for the instance. |
1407 | | /// |
1408 | | /// 1. Passthrough to the next layer if the command is not recognized. |
1409 | | /// |
1410 | | /// # Safety |
1411 | | /// See valid usage of `vkGetInstanceProcAddr` at |
1412 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html>. |
1413 | | #[deny(unsafe_op_in_unsafe_fn)] |
1414 | 2.12k | pub unsafe extern "system" fn get_instance_proc_addr( |
1415 | 2.12k | instance: vk::Instance, |
1416 | 2.12k | p_name: *const c_char, |
1417 | 2.12k | ) -> vk::PFN_vkVoidFunction { |
1418 | 2.12k | // Make sure Global is initialized. |
1419 | 2.12k | let global = Self::instance(); |
1420 | 2.12k | // Safe because the caller is expected to pass in a C string in the name parameter. See |
1421 | 2.12k | // VUID-vkGetInstanceProcAddr-pName-parameter. |
1422 | 2.12k | let name = unsafe { CStr::from_ptr(p_name) }; |
1423 | 2.12k | let name = name.to_str().expect(concat!( |
1424 | 2.12k | "According to VUID-vkGetInstanceProcAddr-pName-parameter, p_name must be a null-", |
1425 | 2.12k | "terminated UTF-8 string." |
1426 | 2.12k | )); |
1427 | 2.12k | // Returns a valid function pointer for global commands all the time, although this behavior |
1428 | 2.12k | // doesn't match the spec. See https://github.com/KhronosGroup/Vulkan-Loader/issues/1537 for |
1429 | 2.12k | // details. |
1430 | 2.12k | match name { |
1431 | 2.12k | "vkGetInstanceProcAddr" => { |
1432 | | return unsafe { |
1433 | 4 | std::mem::transmute::<vk::PFN_vkGetInstanceProcAddr, vk::PFN_vkVoidFunction>( |
1434 | 4 | Self::get_instance_proc_addr, |
1435 | 4 | ) |
1436 | | } |
1437 | | } |
1438 | 2.11k | "vkEnumerateInstanceExtensionProperties" => { |
1439 | | return unsafe { |
1440 | 99 | std::mem::transmute::< |
1441 | 99 | vk::PFN_vkEnumerateInstanceExtensionProperties, |
1442 | 99 | vk::PFN_vkVoidFunction, |
1443 | 99 | >(Self::enumerate_instance_extension_properties) |
1444 | | } |
1445 | | } |
1446 | 2.02k | "vkEnumerateInstanceLayerProperties" => { |
1447 | | return unsafe { |
1448 | 99 | std::mem::transmute::< |
1449 | 99 | vk::PFN_vkEnumerateInstanceLayerProperties, |
1450 | 99 | vk::PFN_vkVoidFunction, |
1451 | 99 | >(Self::enumerate_instance_layer_properties) |
1452 | | } |
1453 | | } |
1454 | 1.92k | "vkCreateInstance" => { |
1455 | | return unsafe { |
1456 | 102 | std::mem::transmute::<vk::PFN_vkCreateInstance, vk::PFN_vkVoidFunction>( |
1457 | 102 | Self::create_instance, |
1458 | 102 | ) |
1459 | | } |
1460 | | } |
1461 | 1.81k | _ => {} |
1462 | 1.81k | } |
1463 | 1.81k | if instance == vk::Instance::null() { |
1464 | | // TODO: allow inteception of vkEnumerateInstanceVersion and handle |
1465 | | // vkEnumerateInstanceVersion properly. For now, we still return NULL for |
1466 | | // vkEnumerateInstanceVersion. The Vulkan loader should cover us for this case. |
1467 | | // Per spec, if instance is NULL, and pName is neither NULL nor a global command, NULL |
1468 | | // should be returned. |
1469 | 99 | return None; |
1470 | 1.72k | } |
1471 | 1.72k | let instance_info = global.get_instance_info(instance)?0 ; |
1472 | 1.72k | if global.get_instance_addr_proc_hooked { |
1473 | 0 | if let LayerResult::Handled(res) = instance_info |
1474 | 0 | .customized_info |
1475 | 0 | .borrow() |
1476 | 0 | .hooks() |
1477 | 0 | .get_instance_proc_addr(name) |
1478 | | { |
1479 | 0 | return res; |
1480 | 0 | } |
1481 | 1.72k | } |
1482 | 1.72k | let get_next_proc_addr = |
1483 | 1.46k | || unsafe { (instance_info.get_instance_proc_addr)(instance, p_name) }; |
1484 | 1.72k | let instance_commands = &instance_info.instance_commands; |
1485 | 1.72k | let instance_command = match instance_commands |
1486 | 13.7k | .binary_search_by_key(&name, |VulkanCommand { name, .. }| name)1.72k |
1487 | | { |
1488 | 1.50k | Ok(index) => Some(&instance_commands[index]), |
1489 | 215 | Err(_) => None, |
1490 | | }; |
1491 | 1.72k | if let Some(instance_command1.50k ) = instance_command { |
1492 | 1.50k | if !instance_command.hooked { |
1493 | 1.19k | return get_next_proc_addr(); |
1494 | 307 | } |
1495 | 307 | let enabled = instance_command.features.is_command_enabled( |
1496 | 307 | &instance_info.api_version, |
1497 | 307 | &instance_info.enabled_extensions, |
1498 | 307 | ); |
1499 | 307 | if !enabled { |
1500 | 50 | return get_next_proc_addr(); |
1501 | 257 | } |
1502 | 257 | return instance_command.proc; |
1503 | 215 | } |
1504 | 215 | let device_commands = &instance_info.device_commands; |
1505 | 215 | let device_command = |
1506 | 2.36k | match device_commands.binary_search_by_key(&name, 215 |VulkanCommand { name, .. }| name)215 { |
1507 | 214 | Ok(index) => Some(&device_commands[index]), |
1508 | 1 | Err(_) => None, |
1509 | | }; |
1510 | 215 | if let Some(device_command214 ) = device_command { |
1511 | 214 | let next_proc_addr = get_next_proc_addr(); |
1512 | 214 | if !device_command.hooked { |
1513 | 153 | return next_proc_addr; |
1514 | 61 | } |
1515 | 61 | let layer_device_extensions: BTreeSet<Extension> = T::manifest() |
1516 | 61 | .device_extensions |
1517 | 61 | .iter() |
1518 | 61 | .map(|ExtensionProperties { name, .. }| name.clone()10 ) |
1519 | 61 | .collect(); |
1520 | | // If the layer supports the command or the next proc addr can find it, this is an |
1521 | | // available device command. |
1522 | 61 | let command_available = device_command |
1523 | 61 | .features |
1524 | 61 | .is_command_enabled(&instance_info.api_version, &layer_device_extensions) |
1525 | 4 | || next_proc_addr.is_some(); |
1526 | 61 | if command_available { |
1527 | 59 | return device_command.proc; |
1528 | | } else { |
1529 | 2 | return next_proc_addr; |
1530 | | } |
1531 | 1 | } |
1532 | 1 | // We don't recognize this command. |
1533 | 1 | get_next_proc_addr() |
1534 | 2.12k | } |
1535 | | |
1536 | | /// The `vkGetDeviceProcAddr` entry point provided by the layer framework. |
1537 | | /// |
1538 | | /// The layer framework will make use of [`Layer::hooked_device_commands`] to decide whether a |
1539 | | /// local function pointer should be returned or should passthrough to the next layer in the |
1540 | | /// call chain. Note that the layer implementation doesn't have to take care of whether the |
1541 | | /// command is enabled(either included in the requested core version or enabled extension), and |
1542 | | /// only needs to use the [`Layer::hooked_device_commands`] interface to express the intent. The |
1543 | | /// layer framework should handle most of the logic(e.g. the requirement of |
1544 | | /// [`LLP_LAYER_18`](<https://github.com/KhronosGroup/Vulkan-Loader/blob/v1.3.261/docs/LoaderLayerInterface.md#pre-instance-functions:~:text=LLP_LAYER_18,Conventions%20and%20Rules>)) |
1545 | | /// here unless the layer implementation decides to intercept `vkGetDeviceProcAddr`. |
1546 | | /// |
1547 | | /// # Safety |
1548 | | /// See valid usage of `vkGetDeviceProcAddr` at |
1549 | | /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetDeviceProcAddr.html>. |
1550 | | #[deny(unsafe_op_in_unsafe_fn)] |
1551 | 5.22k | pub unsafe extern "system" fn get_device_proc_addr( |
1552 | 5.22k | device: vk::Device, |
1553 | 5.22k | p_name: *const c_char, |
1554 | 5.22k | ) -> vk::PFN_vkVoidFunction { |
1555 | 5.22k | let name = unsafe { CStr::from_ptr(p_name) }; |
1556 | 5.22k | let name = name.to_str().expect("name should be a valid UTF-8 string."); |
1557 | 5.22k | let global = Self::instance(); |
1558 | 5.22k | let device_info = global.get_device_info(device)?0 ; |
1559 | 5.22k | if let LayerResult::Handled(res0 ) = device_info |
1560 | 5.22k | .customized_info |
1561 | 5.22k | .borrow() |
1562 | 5.22k | .hooks() |
1563 | 5.22k | .get_device_proc_addr(name) |
1564 | | { |
1565 | 0 | return res; |
1566 | 5.22k | } |
1567 | 5.22k | let get_next_device_proc_addr = |
1568 | 5.19k | || unsafe { (device_info.get_device_proc_addr)(device, p_name) }; |
1569 | 5.22k | let device_commands = &device_info.device_commands; |
1570 | 5.17k | let command = if let Ok(index) = |
1571 | 57.4k | device_commands.binary_search_by_key(&name, 5.22k |VulkanCommand { name, .. }| name)5.22k |
1572 | | { |
1573 | 5.17k | &device_commands[index] |
1574 | | } else { |
1575 | 43 | return get_next_device_proc_addr(); |
1576 | | }; |
1577 | 5.17k | if !command |
1578 | 5.17k | .features |
1579 | 5.17k | .is_command_enabled(&device_info.api_version, &device_info.enabled_extensions) |
1580 | | { |
1581 | 2.12k | return get_next_device_proc_addr(); |
1582 | 3.04k | } |
1583 | 3.04k | if !command.hooked { |
1584 | 3.01k | return get_next_device_proc_addr(); |
1585 | 29 | } |
1586 | 29 | command.proc |
1587 | 5.22k | } |
1588 | | } |
1589 | | |
1590 | | impl<T: Layer> Default for Global<T> { |
1591 | 61 | fn default() -> Self { |
1592 | 61 | let layer_info: T = Default::default(); |
1593 | 61 | let get_instance_addr_proc_hooked = T::GlobalHooksInfo::hooked_commands() |
1594 | 61 | .contains(&LayerVulkanCommand::GetInstanceProcAddr); |
1595 | 61 | Self { |
1596 | 61 | instance_map: Default::default(), |
1597 | 61 | physical_device_map: Default::default(), |
1598 | 61 | device_map: Default::default(), |
1599 | 61 | layer_info, |
1600 | 61 | get_instance_addr_proc_hooked, |
1601 | 61 | } |
1602 | 61 | } |
1603 | | } |
1604 | | |
1605 | | /// A stub struct that intercept no commands, which implements [`GlobalHooks`] and |
1606 | | /// [`GlobalHooksInfo`]. |
1607 | | #[derive(Default)] |
1608 | | pub struct StubGlobalHooks; |
1609 | | |
1610 | 8 | #[auto_globalhooksinfo_impl] |
1611 | | impl GlobalHooks for StubGlobalHooks {} |
1612 | | |
1613 | | /// A stub struct that intercept no commands, which implements [`InstanceHooks`] and |
1614 | | /// [`InstanceInfo`]. |
1615 | | #[derive(Default)] |
1616 | | pub struct StubInstanceInfo; |
1617 | | |
1618 | 5 | #[auto_instanceinfo_impl] |
1619 | | impl InstanceHooks for StubInstanceInfo {} |
1620 | | |
1621 | | /// A stub struct that intercept no commands, which implements [`DeviceHooks`] and [`DeviceInfo`]. |
1622 | | #[derive(Default)] |
1623 | | pub struct StubDeviceInfo; |
1624 | | |
1625 | 189 | #[auto_deviceinfo_impl] |
1626 | | impl DeviceHooks for StubDeviceInfo {} |
1627 | | |
1628 | | #[cfg(test)] |
1629 | | mod tests { |
1630 | | use once_cell::sync::Lazy; |
1631 | | |
1632 | | use crate::test_utils::LayerManifestExt; |
1633 | | use std::{cmp::Ordering, mem::MaybeUninit}; |
1634 | | |
1635 | | use super::*; |
1636 | | |
1637 | | #[test] |
1638 | 1 | fn commands_must_be_sorted() { |
1639 | | #[derive(Default)] |
1640 | | struct TestLayer(StubGlobalHooks); |
1641 | | |
1642 | | impl Layer for TestLayer { |
1643 | | type GlobalHooksInfo = StubGlobalHooks; |
1644 | | type InstanceInfo = StubInstanceInfo; |
1645 | | type DeviceInfo = StubDeviceInfo; |
1646 | | type InstanceInfoContainer = StubInstanceInfo; |
1647 | | type DeviceInfoContainer = StubDeviceInfo; |
1648 | | |
1649 | 2 | fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static { |
1650 | 2 | &*GLOBAL |
1651 | 2 | } |
1652 | | |
1653 | 0 | fn manifest() -> LayerManifest { |
1654 | 0 | LayerManifest::test_default() |
1655 | 0 | } |
1656 | | |
1657 | 0 | fn global_hooks_info(&self) -> &Self::GlobalHooksInfo { |
1658 | 0 | &self.0 |
1659 | 0 | } |
1660 | | |
1661 | 0 | fn create_instance_info( |
1662 | 0 | &self, |
1663 | 0 | _: &vk::InstanceCreateInfo, |
1664 | 0 | _: Option<&vk::AllocationCallbacks>, |
1665 | 0 | _: Arc<ash::Instance>, |
1666 | 0 | _next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, |
1667 | 0 | ) -> Self::InstanceInfoContainer { |
1668 | 0 | Default::default() |
1669 | 0 | } |
1670 | | |
1671 | 0 | fn create_device_info( |
1672 | 0 | &self, |
1673 | 0 | _: vk::PhysicalDevice, |
1674 | 0 | _: &vk::DeviceCreateInfo, |
1675 | 0 | _: Option<&vk::AllocationCallbacks>, |
1676 | 0 | _: Arc<ash::Device>, |
1677 | 0 | _next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr, |
1678 | 0 | ) -> Self::DeviceInfoContainer { |
1679 | 0 | Default::default() |
1680 | 0 | } |
1681 | | } |
1682 | | |
1683 | | static GLOBAL: Lazy<Global<TestLayer>> = Lazy::new(Default::default); |
1684 | | |
1685 | | #[inline] |
1686 | 2 | fn check<T: PartialOrd>(last: &mut T) -> impl FnMut(T) -> bool + '_ { |
1687 | 617 | move |curr| { |
1688 | 617 | if let Some(Ordering::Greater) | None = (*last).partial_cmp(&curr) { |
1689 | 0 | return false; |
1690 | 617 | } |
1691 | 617 | *last = curr; |
1692 | 617 | true |
1693 | 617 | } |
1694 | 2 | } |
1695 | | |
1696 | 1 | let instance_info = Default::default(); |
1697 | 1 | let device_commands = |
1698 | 1 | Global::<TestLayer>::instance().create_device_commands(&instance_info, None); |
1699 | 1 | let mut name_iter = device_commands |
1700 | 1 | .iter() |
1701 | 517 | .map(|VulkanCommand { name, .. }| *name); |
1702 | 1 | let mut last = name_iter.next().unwrap(); |
1703 | 1 | |
1704 | 1 | assert!(name_iter.all(check(&mut last))); |
1705 | | |
1706 | 1 | let instance_commands = |
1707 | 1 | Global::<TestLayer>::instance().create_instance_commands(&instance_info); |
1708 | 1 | let mut name_iter = instance_commands |
1709 | 1 | .iter() |
1710 | 102 | .map(|VulkanCommand { name, .. }| *name); |
1711 | 1 | let mut last = name_iter.next().unwrap(); |
1712 | 1 | |
1713 | 1 | assert!(name_iter.all(check(&mut last))); |
1714 | 1 | } |
1715 | | |
1716 | | #[test] |
1717 | 1 | fn fill_vk_out_array_should_handle_zero_count_correctly() { |
1718 | 1 | let extensions = [ExtensionProperties { |
1719 | 1 | name: Extension::KHRSwapchain, |
1720 | 1 | spec_version: 1, |
1721 | 1 | }] |
1722 | 1 | .into_iter() |
1723 | 1 | .map(Into::<vk::ExtensionProperties>::into) |
1724 | 1 | .collect::<Vec<_>>(); |
1725 | 1 | |
1726 | 1 | let mut count = 0; |
1727 | 1 | assert_eq!( |
1728 | 1 | unsafe { fill_vk_out_array(&extensions, (&mut count).into(), null_mut()) }, |
1729 | 1 | vk::Result::SUCCESS |
1730 | 1 | ); |
1731 | 1 | assert_eq!(count, extensions.len()); |
1732 | | |
1733 | 1 | count = 0; |
1734 | 1 | let mut extension_property = MaybeUninit::<vk::ExtensionProperties>::uninit(); |
1735 | 1 | assert_eq!( |
1736 | 1 | unsafe { |
1737 | 1 | fill_vk_out_array( |
1738 | 1 | &extensions, |
1739 | 1 | (&mut count).into(), |
1740 | 1 | extension_property.as_mut_ptr(), |
1741 | 1 | ) |
1742 | 1 | }, |
1743 | 1 | vk::Result::INCOMPLETE |
1744 | 1 | ); |
1745 | 1 | assert_eq!(count, 0); |
1746 | | |
1747 | 1 | count = 0; |
1748 | 1 | assert_eq!( |
1749 | 1 | unsafe { fill_vk_out_array(&[], (&mut count).into(), extension_property.as_mut_ptr()) }, |
1750 | 1 | vk::Result::SUCCESS |
1751 | 1 | ); |
1752 | 1 | } |
1753 | | |
1754 | | #[test] |
1755 | 1 | fn fill_vk_out_array_should_return_incomplete_with_short_out_array() { |
1756 | 1 | let extensions = [ |
1757 | 1 | ExtensionProperties { |
1758 | 1 | name: Extension::KHRSwapchain, |
1759 | 1 | spec_version: 1, |
1760 | 1 | }, |
1761 | 1 | ExtensionProperties { |
1762 | 1 | name: Extension::KHRSamplerYcbcrConversion, |
1763 | 1 | spec_version: 1, |
1764 | 1 | }, |
1765 | 1 | ] |
1766 | 1 | .into_iter() |
1767 | 1 | .map(Into::<vk::ExtensionProperties>::into) |
1768 | 1 | .collect::<Vec<_>>(); |
1769 | 1 | |
1770 | 1 | let mut count = 1; |
1771 | 1 | let mut extension_property = MaybeUninit::<vk::ExtensionProperties>::uninit(); |
1772 | 1 | assert_eq!( |
1773 | 1 | unsafe { |
1774 | 1 | fill_vk_out_array( |
1775 | 1 | &extensions, |
1776 | 1 | (&mut count).into(), |
1777 | 1 | extension_property.as_mut_ptr(), |
1778 | 1 | ) |
1779 | 1 | }, |
1780 | 1 | vk::Result::INCOMPLETE |
1781 | 1 | ); |
1782 | 1 | assert_eq!(count, 1); |
1783 | 1 | } |
1784 | | } |